package com.azunitech.apm.bundles.shell;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.util.Enumeration;
import java.util.Hashtable;

public class ShellServer <T>
{
	private Hashtable<String, ICommand<T>> m_RegistedCommand = new Hashtable<String, ICommand<T>>();
	private Hashtable<String, ICommand<T>> m_UnActiveRegistedCommand = new Hashtable<String, ICommand<T>>();
	private Binding m_binding;
	private T m_T;
	private int m_Port;
	private int m_MaxConn;
	private CommandHELP m_Help;
	private StringBuffer m_Buffer = new StringBuffer();
	private State m_State = State.None;
	
	public ShellServer( int port, int maxConn)
	{
		this.m_Port = port;
		this.m_MaxConn = maxConn;
		m_Help = new CommandHELP();
	}
	
	public void setPort( int port)
	{
		this.m_Port = port;
	}
	
	public void registerTarget( T t)
	{
		this.m_T = t;
	}
	
	public void start() throws IOException
	{
		if ( this.m_State == State.Running)
		{
			return;
		}
		this.m_RegistedCommand.put(this.m_Help.getKey(), m_Help);
		m_binding = getTelnetBinding(m_T, m_Port, m_MaxConn);
	    m_binding.start();
	    this.m_State = State.Running;
	}
	
	public void stop() throws InterruptedException 
	{
		if ( this.m_State == State.Stoppped)
		{
			return;
		}
		
		if ( this.m_binding != null)
		{
			m_binding.stop();
		}
	    this.m_State = State.Stoppped;
	}

	public synchronized void activeCommand( String key)
	{
		if ( this.m_UnActiveRegistedCommand.containsKey(key.trim()))
		{
			ICommand<T> cmd = this.m_UnActiveRegistedCommand.get(key);
			this.registedCommand(cmd);
			this.m_UnActiveRegistedCommand.remove(key);
		}
	}
	
	public void registedCommand ( ICommand<T> cmd)
	{
		this.registedCommand(cmd, true);
	}

	public String getUnactiveCommands()
	{
		StringBuffer buffer = new StringBuffer();
		Enumeration<String> enumList = this.m_UnActiveRegistedCommand.keys();
		while( enumList.hasMoreElements())
		{
			String key = enumList.nextElement();
			buffer.append(key).append(",");
		}
		
		String result = buffer.toString();
		if ( result.length() >= 1)
		{
			result = result.substring(0, result.length() - 1);
		}
		
		return result;
	}
	
	public void registedCommand ( ICommand<T> cmd, boolean isActive )
	{
		if (isActive)
		{
			if (!this.m_RegistedCommand.containsKey(cmd.getKey()))
			{
				this.m_RegistedCommand.put(cmd.getKey(), cmd);
				m_Buffer.append( cmd.getKey()).append("--").append(cmd.getDesc()).append("\r\n");
			}
		}
		else
		{
			if (!this.m_UnActiveRegistedCommand.containsKey(cmd.getKey()))
			{
				String key = cmd.getKey();
				this.m_UnActiveRegistedCommand.put(key, cmd);
			}
		}
	}
	
	private Binding getTelnetBinding(T target, int port, int max) throws IOException 
	{
		return new TelnetBinding<T>(getExecuteCommand(target),new ServerSocket(port), max);
	}
	
	private ICommand<T> getExecuteCommand(T target ) throws IOException 
	{
		ICommand<T> command = new CommandHELP().setTarget(m_T);
		return command;
	}
	
	public String getState()
	{
		return this.m_State.toString();
	}
	
	private class CommandHELP extends BasicCommand<T>
	{
		private static final String CMD_KEY = "help";
		
		public CommandHELP() 
		{
		}
		
		public void exec(String args, PrintStream out, PrintStream err)
		{
			if ( args == null || args.length() == 0)
			{
				return;
			}
			
			//Add parameter parser
			if ( args.startsWith(CommandHELP.CMD_KEY))
			{
				out.println(m_Buffer.toString());
			}
			else
			{
				String cmdKey = args.substring(0, 4);
				BasicCommand<T> cmd = (BasicCommand<T>) m_RegistedCommand.get(cmdKey);
				if ( cmd != null)
				{
					try 
					{
						cmd.setTarget(this.m_target);
						cmd.exec(args, out, err);
					} 
					catch (Exception e) 
					{
						e.printStackTrace();
					}
				}
			}
		}

		public String getKey() 
		{
			return CommandHELP.CMD_KEY;
		}

		public String getDesc() 
		{
			return ": \r\n";
		}
	}
	
	public enum State
	{
		Stoppped,
		Running,
		None
	}
}
